package com.lightsaber.cut.entity;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import com.lightsaber.cut.App;

public class ComponentPool {

    List<Component> componentPool = new ArrayList<Component>();

    public List<Component> getComponentPool() {

        return componentPool;
    }

    public void setComponentPool(List<Component> componentPool) {

        this.componentPool = componentPool;
    }

    public void put(Component component) {

        this.componentPool.add(component);
    }

    public Integer getTotalLength() {

        Integer length = 0;
        for (Component component : componentPool) {
            length += component.getLength();
        }

        return length;
    }

    /**
     * 返回预选总长
     * 
     * @return 预选总长
     */
    public Integer getReserveLength() {

        Integer length = 0;
        for (Component component : componentPool) {
            if (component.isReserve()) {
                length += component.getLength();
            }
        }

        return length;
    }

    public void sort() {
        Collections.sort(componentPool);
    }

    @Override
    public String toString() {

        StringBuilder sb = new StringBuilder("余下尺寸: ");
        for (Component co : componentPool) {
            sb.append(co.getLength() * 1.0 / 1000 + "(" + co.getSequence() + "), ");
        }
        sb.append("; ");
        sb.append("总长: " + this.getTotalLength() * 1.0 / 1000 + "\r\n");

        return sb.toString();
    }

    @Override
    public ComponentPool clone() {

        ComponentPool clonePool = new ComponentPool();
        for (Component comp : componentPool) {
            Component compClone = new Component();
            compClone.setLength(comp.getLength());
            compClone.setSequence(comp.getSequence());
            clonePool.put(compClone);
        }

        return clonePool;
    }

    /**
     * 最多克隆四十个
     * 
     * @return
     */
    public ComponentPool cloneGroup(int group) {

        ComponentPool clonePool = new ComponentPool();

        for (int i = App.POOL_GROUP_SIZE * group; i < App.POOL_GROUP_SIZE * (group + 1); i++) {
            if (i >= this.componentPool.size()) {
                break;
            }

            Component compClone = new Component();
            compClone.setLength(this.componentPool.get(i).getLength());
            compClone.setSequence(this.componentPool.get(i).getSequence());
            clonePool.put(compClone);
        }

        return clonePool;
    }

    /**
     * 切割一组整料
     * 
     * @param originalType
     *            整料长
     * @param maxSurplus
     *            允许最大余料
     * @param n
     *            直切n根
     * @return
     * @throws Exception
     */
    public List<Original> cut(Integer originalType, Integer maxSurplus, Integer n) throws Exception {
        System.out.println(String.format("------------------连续切割%d的%d根-----------------",
                originalType, n));
        // 要切割的整料
        List<Original> originals = new ArrayList<Original>();

        if (n == 0) {
            return originals;
        }

        for (int i = 0; i < n; i++) {
            Original o = this.cutOne(originalType, maxSurplus);
            if (o == null) {// 再也无法组合
                break;
            }
            originals.add(o);
        }

        for (int i = 0; i < originals.size(); i++) {
            System.out.print(originals.get(i).toString());
        }
        System.out.println(this.toString());

        return originals;
    }

    /**
     * 切割一组整料
     * 
     * @param originalType
     *            整料长
     * @param maxSurplus
     *            允许最大余料
     * @return
     * @throws Exception
     */
    public List<Original> cut(Integer originalType, Integer maxSurplus) throws Exception {
        System.out.print("连续切割" + originalType + "的料");

        // 要切割的整料
        List<Original> originals = new ArrayList<Original>();

        while (true) {
            Original o = this.cutOne(originalType, maxSurplus);
            if (o == null) {// 再也无法组合
                break;
            }
            originals.add(o);
        }

        System.out.println(originals.size() + "根");
        for (int i = 0; i < originals.size(); i++) {
            System.out.print(originals.get(i).toString());
        }
        System.out.println(this.toString());

        return originals;
    }

    /**
     * 切割一根整料
     * 
     * 情况1：尺寸和 < originalLength - maxSurplus 时, 立即返回null<br>
     * 情况2：(originalLength - maxSurplus) <= 尺寸和 <= originalLength 时, 立即全选组成一根整料<br>
     * 情况3：尺寸和 > originalLength 时, 递归排列组合<br>
     * 
     * @param originalLength
     * @param maxSurplus
     *            整料长度
     * @return
     * @throws Exception
     *             允许最大余量
     */
    private Original cutOne(Integer originalLength, Integer maxSurplus) throws Exception {

        // 情况1
        if (this.getTotalLength() < (originalLength - maxSurplus)) {
            return null;
        }

        // 情况2, 预选全部
        if (this.getTotalLength() <= originalLength) {
            for (Component co : this.componentPool) {
                co.setReserve(true);
            }
        }

        // 情况3, 预选部分
        if (this.getTotalLength() > (originalLength)) {
            if (!recurseSelect(originalLength, maxSurplus)) {
                return null;
            }
        }

        // 确定预选
        Original original = new Original(originalLength);
        for (int i = this.componentPool.size() - 1; i >= 0; i--) {
            Component co = this.componentPool.get(i);

            if (co.isReserve()) {
                this.componentPool.remove(co);
                original.cut(co);
            }
        }

        return original;
    }

    /**
     * @param originalLength
     * @param maxSurplus
     *            最大余量
     * @return true 可以下料了, false： 怎么加都溢出了
     */
    private boolean recurseSelect(Integer originalLength, Integer maxSurplus) {

        Component co = null;

        // 开始为这一层选一个尺寸
        for (int i = 0; i < this.componentPool.size(); i++) {
            co = this.componentPool.get(i);

            if (!co.isReserve()) {
                // step1: 选进一个
                co.setReserve(true);

                // step2: 看是否合适
                if (this.getReserveLength() <= originalLength) {// 预选总长小于料长
                    if (this.getReserveLength() >= (originalLength - maxSurplus)) {// 满足下料要求
                        return true;

                    } else {

                        // step3: 码到上层继续加料
                        boolean nextLevelRight = recurseSelect(originalLength, maxSurplus);

                        if (nextLevelRight) {
                            return true;
                        } else {
                            // 上一层怎么加都溢出了， 本层本层退掉，换下一个
                            co.setReserve(false);
                            continue;
                        }
                    }

                } else {
                    // 加进来大的, 溢出了, 退掉它, 换下一个
                    co.setReserve(false);
                    continue;
                }
            }
        }

        // -------------------------------------------------------------------------------
        // 到这里有几种情况: 1. 尺寸用完了还没有填满一根整料; 2. 所有的余料逐一加进来都会溢出

        boolean re = true;// 情况1
        for (Component _co : this.componentPool) {
            if (!_co.isReserve()) {
                re = false;// 情况2
                break;
            }
        }
        return re;
    }

    // ----------------------- 预料是固定尺寸的切割-----------------------START
    /**
     * 切割一组整料
     * 
     * @param originalType
     *            整料长
     * @param maxSurplus
     *            固定余料
     * @return
     * @throws Exception
     */
    public List<Original> cutFix(Integer originalType, List<Integer> fixSurplus) throws Exception {

        // 要切割的整料
        List<Original> originals = new ArrayList<Original>();

        while (true) {
            Original o = this.cutOneFix(originalType, fixSurplus);
            if (o == null) {// 再也无法组合
                break;
            }
            originals.add(o);
        }

        System.out.println("连续切割" + originalType + "的料, 余固定长度.........");
        for (int i = 0; i < originals.size(); i++) {
            System.out.print(originals.get(i).toString());
        }
        System.out.println(this.toString());

        return originals;
    }

    /**
     * 切割一根整料
     * 
     * 情况1：尺寸和 < originalLength - maxSurplus 时, 立即返回null<br>
     * 情况2：(originalLength - maxSurplus) <= 尺寸和 <= originalLength 时, 立即全选组成一根整料<br>
     * 情况3：尺寸和 > originalLength 时, 递归排列组合<br>
     * 
     * @param originalLength
     * @param fixSurplus
     *            固定预料
     * @return
     * @throws Exception
     *             允许最大余量
     */
    private Original cutOneFix(Integer originalLength, List<Integer> fixSurpluses) throws Exception {

        for (Integer fixSurplus : fixSurpluses) {
            Integer fixLength = originalLength - fixSurplus;

            // 情况1
            if (this.getTotalLength() < (fixLength - App.FIX_MOVE)) {
                continue;
            }

            // 情况2, 预选全部
            if (this.getTotalLength() <= (fixLength + App.FIX_MOVE)) {
                for (Component co : this.componentPool) {
                    co.setReserve(true);
                }
            }

            // 情况3, 预选部分
            if (this.getTotalLength() > (fixLength + App.FIX_MOVE)) {
                if (!recurseSelectFix(originalLength, fixLength)) {
                    continue;
                }
            }

            // 确定预选
            Original original = new Original(originalLength);
            for (int i = this.componentPool.size() - 1; i >= 0; i--) {
                Component co = this.componentPool.get(i);

                if (co.isReserve()) {
                    this.componentPool.remove(co);
                    original.cut(co);
                }
            }
            return original;
        }

        return null;
    }

    /**
     * @param originalLength
     * @param maxSurplus
     *            余固定料后的固定长度
     * @return true 可以下料了, false： 怎么加都溢出了
     */
    private boolean recurseSelectFix(Integer originalLength, Integer fixLength) {

        Component co = null;

        // 开始为这一层选一个尺寸
        for (int i = 0; i < this.componentPool.size(); i++) {
            co = this.componentPool.get(i);

            if (!co.isReserve()) {
                // step1: 选进一个
                co.setReserve(true);

                // step2: 看是否合适
                if (this.getReserveLength() <= (fixLength + App.FIX_MOVE)) {// 预选总长小于料长
                    if (this.getReserveLength() >= (fixLength - App.FIX_MOVE)) {// 满足下料要求
                        return true;

                    } else {

                        // step3: 码到上层继续加料
                        boolean nextLevelRight = recurseSelectFix(originalLength, fixLength);

                        if (nextLevelRight) {
                            return true;
                        } else {
                            // 上一层怎么加都溢出了， 本层本层退掉，换下一个
                            co.setReserve(false);
                            continue;
                        }
                    }

                } else {
                    // 加进来大的, 溢出了, 退掉它, 换下一个
                    co.setReserve(false);
                    continue;
                }
            }
        }

        // 到这里只有一种情况: 所有的余料逐一加进来都会溢出
        return false;
    }
    // ----------------------- 预料是固定尺寸的切割-------------------------END
}
